import { GraphQLResolveInfo } from "graphql";
import { AdminRoleEnum } from "@prisma/client";
import { NexusGenFieldTypes } from "typings";
import { AuthenticationError } from "apollo-server-koa";
import { Service } from "egg";

type RolesMap = AdminRoleEnum[] | "*" | "onlyLogin" | "onlyUser";

type TypesOperationsRolesMap = {
  Mutation: {
    [key in keyof Partial<NexusGenFieldTypes["Mutation"]>]: RolesMap;
  };
  Query: {
    [key in keyof Partial<NexusGenFieldTypes["Query"]>]: RolesMap;
  };
};

// 操作类型表
const typesOperationsRolesMap: TypesOperationsRolesMap = {
  // 变更
  Mutation: {
    loginWithAccountAndPassword: "*",

    // 应用
    createStore: ["SUPERADMIN"],
  },

  // 查询
  Query: {
    storeConnection: ["SUPERADMIN"],
  },
};

/**
 * 用户业务逻辑处理类
 */
export default class AuthenticateService extends Service {
  // 进行身份验证
  async authenticate(info: GraphQLResolveInfo) {
    const {
      fieldName: operation,
      parentType: { name: type },
    } = info;

    // @ts-ignore
    const operationsRolesMap = typesOperationsRolesMap[type];

    if (!operationsRolesMap) {
      throw new AuthenticationError("Not found type.");
    }

    const roles = operationsRolesMap[operation] as RolesMap;

    if (!roles) {
      throw new AuthenticationError("Not found operation.");
    }

    if (roles === "*") {
      return true;
    }

    const { token, db } = this.ctx;

    if (!token) {
      throw new AuthenticationError("Must authenticate.");
    }

    const {
      sub: { source, adminId, userId },
    } = token;

    switch (source) {
      case "CONSOLE": {
        if (roles === "onlyUser") {
          throw new AuthenticationError("OnlyUser");
        }
        const userRole = await db.admin.findUnique({ where: { id: adminId } });
        if (!userRole) {
          throw new AuthenticationError("Not found userRole.");
        }
        if (roles === "onlyLogin") {
          return true;
        }
        const hasPermission = roles.find((role) => userRole.role === role);
        if (!hasPermission) {
          this.ctx.logger.error("operation", operation);

          throw new AuthenticationError("Not have permission.");
        }

        break;
      }

      default: {
        if (roles === "onlyLogin") {
          return true;
        }
        if (!userId) {
          throw new AuthenticationError("Must authenticate.");
        }
        if (roles !== "onlyUser") {
          throw new AuthenticationError("onlyAdmin");
        }
      }
    }

    return true;
  }
}
